######################################################################
# Parent class for the Data object
# (to sit inside <EP::Common::DBI::Dataset>)
######################################################################
package EP::Core::Dataset::Data;

use strict;

# sub: new
#   the constructor for Data.
# parameters:
#   owner_dataset - reference to owner dataset of this Data object (TODO: can it be omitted?)
sub new
    {
    my $proto = shift;
    my %params = @_;

    my $self = {};
    $self->{owner} = $params{owner_dataset};

    my $class = ref ( $proto ) || $proto;
    bless $self, $class;

    $self->initialize(%params);

    return $self;
    }

sub initialize       {};
sub retrieve         { die "Override me." };
sub update           { die "Override me." };
sub delete           { die "Override me." };
sub process_metadata { die "Override me." };

# sub: update processed metadata
sub update_processed_metadata
    {
    my $self = shift;
    my $m = shift;
    
    foreach my $metadata_type_name ( keys %$m )
        {
        warn "For $metadata_type_name - $m->{$metadata_type_name}" if $metadata_type_name eq "factors";
        my $metadata_type = EP::Common::DBI::Dataset_Metadata_Type->find_or_create( { name => $metadata_type_name } );

        my $metadata_value = $m->{$metadata_type_name};
        $metadata_value = $m->{$metadata_type_name}->sclr if ref ( $m->{$metadata_type_name} ) =~ /PDL/;

        # see if metadata of this type has already been defined, if yes - reset its value, if no - define and set the new value
        my ( $metadata ) = $self->{owner}->metadata( type => $metadata_type->id );

        if ( $metadata )
            {
            my ( $value ) = $metadata->values;
            $value->value ( "$metadata_value" );
            $value->update;
            }
        else
            {
            $metadata = $self->{owner}->add_to_metadata( { type => $metadata_type->id } );
            $metadata->add_to_values( { value => $metadata_value } );
            my ( $value ) = $metadata->values;
            $value->update;
            }
        }
    }


# sub: map_gene_ids_for_GO
#   tries to map a set of internal gene (row) ids for GeneOntology, using species-specific metadata
# params:
#   gene_ids - array ref of internal gene/row identifiers (numbers from 0 to ..)
# returns:
#   all the identifiers for the genes corresponding to the given
#   ids. this means: if there's a preferred identifier for this
#   species and dataset, it'll return just those. if not, it'll return
#   all identifiers of the type given in SPECIES_METADATA
#   annot_idents. if that is not available, will look for any
#   BASE_ANNOT_IDENTS identifiers. last resort: the first column.
sub map_gene_ids_for_GO
    {
    my $self = shift;
    my ($gene_ids) = @_;

    my $rows = $self->rows();

    # if nothing passed to this function, map ALL the gene ids
    if ( not defined $gene_ids )
        {
        my @all_gene_ids = keys %{$rows};
        $gene_ids = \@all_gene_ids;
        }

    my $species = $self->{owner}->species;
    my @mapped_gene_ids;

    my @annot_idxs;

    warn "Will look for identifiers for species $species in " . join ( ", ", $rows->{0}->{full_annot}->Keys );

    # obtain indices for identifiers to retrieve
    if ( exists $EP::Config::h->{SPECIES_METADATA}->{$species} )
        {
        # try to get index for preferred identifier, if one is defined for this species
        if ( exists $EP::Config::h->{SPECIES_METADATA}->{$species}->{preferred_ident} )
            {
            @annot_idxs = $rows->{0}->{full_annot}->Indices( $EP::Config::h->{SPECIES_METADATA}->{$species}->{preferred_ident} );
            }

        # if we got nothing out of the preferred identifier, try the species-specific annotation identifiers
        if ( scalar @annot_idxs == 0 )
            {
            @annot_idxs = $rows->{0}->{full_annot}->Indices( @{$EP::Config::h->{SPECIES_METADATA}->{$species}->{annot_idents}} );
            }
        }

    # if we didn't get anything out of species-specific things, try more general - base annotation identifiers
    if ( scalar @annot_idxs == 0 )
        {
        @annot_idxs = $rows->{0}->{full_annot}->Indices ( @{$EP::Config::h->{BASE_ANNOT_IDENTS}} );
        }

    # if we still have nothing - just use 0 - first column of annotations
    if ( scalar @annot_idxs == 0 )
        {
        @annot_idxs = ( 0 );
        }

    my %uniq_annot_idxs;
    @uniq_annot_idxs{@annot_idxs} = 1;
    delete $uniq_annot_idxs{""};

    warn join (",", @annot_idxs ). " GOT KEYS: " . join ( "," , keys %uniq_annot_idxs );
    warn "Wound up using " . join ( ", ", $rows->{0}->{full_annot}->Keys( keys %uniq_annot_idxs ) );

    foreach my $gene_id ( @$gene_ids )
        {
        push @mapped_gene_ids, $rows->{$gene_id}->{full_annot}->Values( keys %uniq_annot_idxs );
        }

    my %uniq_gene_ids;
    @uniq_gene_ids{@mapped_gene_ids} = 1;

    warn "Mapped " . scalar @$gene_ids . " genes and got " . scalar(keys(%uniq_gene_ids)) . " unique GO-able gene ids";
#   warn join ",", keys %uniq_gene_ids;

    return [ keys %uniq_gene_ids ];
    }

1;
